Skip to content

Sync mad-rccl with develop - #245

Merged
i-kosarev merged 6 commits into
ROCm:mad-rcclfrom
i-kosarev:sync/develop-into-mad-rccl
Sep 2, 2026
Merged

Sync mad-rccl with develop#245
i-kosarev merged 6 commits into
ROCm:mad-rcclfrom
i-kosarev:sync/develop-into-mad-rccl

Conversation

@i-kosarev

@i-kosarev i-kosarev commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Supersedes #244, which cannot be made mergeable without committing to ROCm/MAD:develop itself: its head branch is develop, so resolving conflicts there would push a mad-rccl -> develop merge onto a protected shared branch — the opposite direction to the sync, and something this repo has never done (every previous sync, e.g. #234, merged develop into mad-rccl). This branch does it the usual way round, so develop is untouched. All five commits from #244 are included.

After this, mad-rccl is 9 ahead, 0 behind develop: develop becomes a full ancestor.

Conflicts

Three files, all "both sides added here":

  • .gitignore — keep both rule blocks.
  • scripts/sglang_disagg/models.json — reconciled by unioning the card lists by name rather than editing markers, then checked nothing either side added was dropped. 21 + 18 over a common 16 → 23 cards; develop contributes pyt_sglang_disagg_mori_io_agentic_qwen3-32b and pyt_sglang_disagg_mori_dp_agentic_deepseek-v3.
  • scripts/sglang_disagg/sglang_disagg_mori_io_ep.sh — take develop on both hunks. The first is a pure addition (agentic Prometheus metrics). The second is develop generalizing the hardcoded benchmark_xPyD.sh into ${_bench_file} for the AgentX suite; that variable is defined a few lines up by another, non-conflicting develop hunk and defaults to benchmark_xPyD.sh, so taking develop's side is backwards compatible.

Verified: no markers left, shell and both JSON files parse, and both sides survive in the launcher — mad-rccl's arch gate, Kimi MLA guard and the three bounded/checked barrier calls, alongside develop's AGENTIC_SERVER_METRICS and BENCHMARK_SCRIPT_FILE hook.

Also: the MoRI overlay build is currently broken at its own default pin

Ported from MAD-private aicomnet_dev_public_gpt_oss (bb7ce03). mori's src/umbp/CMakeLists.txt calls add_subdirectory(tests) unconditionally, ignoring BUILD_TESTS. Those tests use gtest_discover_tests() in POST_BUILD mode, which executes each freshly-linked test binary to enumerate cases; the binaries link the ROCm/HIP runtime and abort inside docker build, which has no GPU (RUN cannot pass --gpus, so /dev/kfd and /dev/dri are absent). The empty output breaks CMake's string(JSON ...) and fails the amd_mori wheel build.

So ENABLE_MORI_OVERLAY=1 — the documented knob for A/B-ing a MoRI commit — cannot build at MORI_COMMIT=a14e6992. Confirmed the unconditional add_subdirectory(tests) is still present at that pin and already fixed on mori main, so the patch greps before it seds and self-disables to MORI_UMBP_TESTS_GUARD_SKIPPED once the pin moves forward.

Reproduced end to end: a docker build with ENABLE_MORI_OVERLAY=1 BUILD_GPU_TARGETS=gfx942 on the -mi30x base logs MORI_UMBP_TESTS_GUARDED (add_subdirectory(tests) now gated by BUILD_TESTS), then builds the wheel and reaches MORI_OK 1.2.0, exit 0. What this does not show is the negative control — no build was run with the guard removed, so the evidence that it is load-bearing is the trigger condition plus the original report, not a failure observed here.

The default path (ENABLE_MORI_OVERLAY=0, MoRI taken from the sgl-dev base) is unaffected either way.

MIR-AMD and others added 5 commits August 28, 2026 17:51
…ement (ROCm#206)

* vllm_dissag: GLM-5.1-FP8 (MLA+DSA) MoRI-EP WideEP disaggregated enablement

Adds GLM-5.1-FP8 (GlmMoeDsaForCausalLM = MLA + DeepSeek Sparse Attention) to the
MoRI-EP WideEP disaggregated serving path, stacked on the ROCm#171 unified launcher.
Fully isolated from DeepSeek-V3/R1: GLM gets its own image + a MODEL_NAME-gated
runtime path, so existing models are byte-identical to develop.

Defects fixed (validated 1P/1D EP8 + 2P/2D EP16, NIAH 2k-35k = 10/10, no crash):
- Long-context accuracy collapse: vLLM #47766 cache-key fix keeps the persistent
  sparse-MLA kernel ON (keys metadata on per-request context+query len).
- 8k disagg prefill crash: DSA adds a 2nd (indexer) KV cache per layer that the
  single-geometry MoRIIO connector never transferred; paired + shipped prefill->
  decode. Plus DSA invalid-token kernel fix (#45324) and shik-latest DP-notify.

Changes:
- docker/vllm_disagg_inference.glmv5.1.ubuntu.amd.Dockerfile: NEW per-model image
  (raviguptaamd/vllm glm5.1-dsa-wideEP_on_shik_latest + aiter e03fa6040 + mori
  42e895472b08 + router). The base vllm_disagg_inference Dockerfile (DSV3/R1) is
  left untouched. Future models add their own Dockerfile the same way.
- models.json: card pyt_vllm_disagg_mori_glm-5.1-fp8 (GLM_SKIP_PATCHERS=1: image
  carries the DSA fixes in-source).
- models.yaml: GLM-5.1-FP8 recipe (block=1, AITER MLA on, eager, mori backends).
  DeepSeek-V3 dp: caps (--max-num-seqs 64 --max-model-len 32768) to bound the
  newer base's decode logits workspace (isolated to the DSV3 entry).
- connectors/moriio.sh: MODEL_NAME-gated GLM DSA runtime patchers (pure no-op for
  other models); GLM_SKIP_PATCHERS switch for baked-fix images.
- 9 idempotent, anchor-based, self-skipping GLM DSA patcher scripts.

KNOWN OPEN DEFECT (future work): 4P/4D EP32 emits corrupted tokens at all context
lengths (suspect moriep all-to-all combine at scale); use 1P/1D and 2P/2D.

Co-Authored-By: Claude <noreply@anthropic.com>

* vllm_dissag: make NIAH harness thinking-model-aware (GLM-5.1)

benchmark_niah.py mis-scored thinking models: it never disabled thinking and
read only content + reasoning_content. GLM-5.1 emits chain-of-thought into the
`reasoning` field and leaves `content` empty until the final answer, so with a
small max_tokens the answer never lands in content -> a false 0/10 even when
generation is correct.

- Add chat_template_kwargs.enable_thinking=false so the answer goes to content.
- Also score the `reasoning` field as a fallback.

Verified against GLM-5.1-FP8: correct 9-10/10 retrieval across 2k-35k on all
tested topologies (EP8/EP16/EP32) after the fix.

Co-Authored-By: Claude <noreply@anthropic.com>

* vllm_dissag: NIAH multi-seed support (NIAH_SEEDS) for variance-aware accuracy

Needle layout is seeded, so a single run is deterministic (bit-exact on the same
stack) but can't tell a real accuracy dip from single-needle variance. Add
NIAH_SEEDS (default 0,1,2) to run each context length across multiple needle
layouts; the summary now reports mean/min/max across seeds. Backward compatible:
NIAH_SEEDS=0 reproduces the prior single-seed behavior.

Co-Authored-By: Claude <noreply@anthropic.com>

* vllm_dissag: NIAH gate robust to cold-start JIT (warmup + readiness probe)

On a freshly-booted node the first request of each context shape pays the full
JIT/kernel-autotune compile (minutes). The NIAH harness scored the FIRST request,
so cold compile landed on a scored/gated request -> false 0/10 or timeout, failing
the accuracy gate and skipping the perf sweep. Root-caused by reproducing on a cold
boot (0 results) vs a warm server (all pass) on the same image.

Fixes:
- benchmark_niah.py: add a warmup pass (NIAH_WARMUP=1 default) — one throwaway
  request per context length before scoring, with a generous timeout, failures
  tolerated. Scored requests are then always warm.
- benchmark_niah.py: distinguish TIMEOUT/ERROR from a wrong answer. Timeouts return
  a sentinel (excluded from mean, never counted as 0/10); summary flags NO-RESULT
  with guidance instead of silently reporting 0.
- benchmark_niah.sh: replace the blind `sleep 10` with a /v1/models readiness poll
  (up to 5 min), and forward NIAH_WARMUP.

Verified: patched harness on the warm server passes 10/10; cold-boot repro no longer
produces false 0/10 because compile happens in the warmup pass.

Co-Authored-By: Claude <noreply@anthropic.com>

* [GLM-5.1] v0.27 level-set: 3.4x decode speedup, 200K context, RDMA + launcher fixes

Validated on MI300X, 8 nodes, image rocmshared/pytorch-private:glm5.1-vllm027-b8
(base ci_base-dedbf6be8b + vLLM raviguptaamd/vllm@glm5.1-dsa-wideEP_on_vllm-v0.27
+ aiter e03fa6040 + MoRI 42e895472b08 + router ROCm#181).

PERF FIX (models.yaml decode.dp) -- the headline change:
  --max-num-batched-tokens 2048 on the DECODE role only.
  max_num_batched_tokens is a chunked-prefill SCHEDULER knob, but it also sizes the
  MoRI EP dispatch buffer (fused_moe/layer.py -> all2all_utils.py -> all2all.py
  max_num_inp_token_per_rank). At the 8192 default a decode instance ran an
  8192-token-wide all2all every step, per layer, x78 layers, while decoding a handful
  of tokens: a fixed ~302ms/step floor, ~320x this model's HBM-bandwidth bound.
  Prefill keeps 8192 (it genuinely dispatches wide batches).

  1024/64 con=8, warm:      TPOT        TTFT        out tok/s
    1P/1D  302 ->  88.0 ms   2431 -> 906 ms   24.9 -> 78.8
    2P/2D  302 ->  94.1 ms          1633 ms          66.7
  Published reference: 1P/1D ~89ms, 2P/2D ~91ms -> matched within 3%.
  Accuracy unaffected: NIAH 2k-200k clean on both topologies, no length collapse,
  memfault=0, latencies equal-or-better at every length. 200K validated (5.7x beyond
  the previously published 35K ceiling).

Dockerfile: base -> ci_base-dedbf6be8b (matches the fork's upstream base), VLLM_REF ->
  the v0.27 branch, and WITH_MORI_BUILD/WITH_AITER_BUILD now default to 1 so a plain
  `docker build` reproduces the validated stack. Previously they defaulted to 0, which
  silently used the base's bundled aiter 0.1.19 -- that GPU-faults on the GLM DSA decode
  kernel. The pinned aiter e03fa6040 / MoRI 42e895472b08 must not be bumped without
  re-running long-context NIAH.

connectors/moriio.sh: per-role env split (PREFILL_*/DECODE_* -> VLLM_MORI_*), mirroring
  the existing PREFILL/DECODE_MORI_BACKEND pattern -- models.yaml env: applies to BOTH
  roles, but prefill and decode need opposite values here. Also injects
  use_inductor_graph_partition (pairs with the vLLM splitting_ops fix).

connectors/moriio.env: RDMA fabric -- MORI_IB_GID_INDEX=3 (RoCEv2 IPv4),
  MORI_RDMA_DEVICES/NCCL_IB_HCA restricted to the 8 GPU-local NICs (leaving the mgmt
  NICs in makes QPs form over a non-routable fabric -> ibverbs.cpp:189 timeouts),
  NCCL/GLOO control sockets on eth0.

run_xPyD_models.slurm: libionic bind-mount requires a regular file after symlink resolve
  (a dangling symlink gave "OCI runtime create ... not a directory", container exit 125);
  prefer FABRIC_SUBNET over `hostname -I` first IP (nodes list a 10.224 overlay first,
  which made the socket_barrier advertise an unreachable NIC -> "Waiting for nodes" hang);
  GLM_KERNEL_PATCH/GLM_BACKEND_PATCH bind-mount hooks to test .py fixes without a rebuild;
  forward the new per-role env keys.

vllm_disagg.sh: same FABRIC_SUBNET IP-selection fix for host_ip.

benchmark_xPyD.sh: per-shape warmup at the REAL isl/osl before each shape's cells. The
  global warmup is isl=osl=32/con=1, which never exercises a 1024/8192/28672 prefill path
  or the decode cudagraph batch sizes, so the first measured cell absorbed residual JIT
  (observed 302ms vs ~88ms steady-state). Warmup output goes to a separate
  _SHAPEWARMUP.log so it cannot pollute the CSV.

models.yaml (GLM-5.1-FP8): decode.dp perf fix above; recipe = prefill eager +
  mori_high_throughput, decode PIECEWISE cudagraph + mori_low_latency; VLLM_USE_LAYERNAME=0;
  VLLM_SPARSE_INDEXER_MAX_LOGITS_MB=64; NCCL heartbeat/timeout knobs for long-context
  collectives.

Full operational playbook (including the dead ends) in skills_vllm_disagg.md.

Co-Authored-By: Claude <noreply@anthropic.com>

* [GLM-5.1] Default WITH_NIXL=0 (lean MoRI-EP-only image)

GLM-5.1 is served over MoRI-EP (all2all) + MoRI-IO (KV transfer); the rixl connector's
UCX/RIXL/rocSHMEM/DeepEP stack is never selected by this recipe, so building it only
lengthens the build and ships unused transports.

0 is also how the validated image was actually built: glm5.1-vllm027-b8 used
--build-arg WITH_NIXL=0, so the previous default of 1 meant a plain `docker build`
did NOT reproduce the tested artifact. With this change, and with
WITH_MORI_BUILD/WITH_AITER_BUILD already defaulting to 1, a no-flag build now matches
the validated stack exactly.

Set --build-arg WITH_NIXL=1 if you need the rixl connector from this same Dockerfile.

Co-Authored-By: Claude <noreply@anthropic.com>

* [GLM-5.1] Add long-context NIAH harness + vllm-disagg operational playbook

niah_200k.py: needle-in-a-haystack sweep that validated GLM-5.1-FP8 to 200,049 tokens
on both 1P/1D (EP8) and 2P/2D (EP16). Reports found/10, latency, and the server-reported
prompt_tokens per length, and writes JSON. Model id is overridable via NIAH_MODEL so it
is not GLM-specific. The existing benchmark_niah.* stop well short of this range; this
covers the 64k-200k band.

skills_vllm_disagg.md: operational playbook for vLLM PD-disaggregated WideEP on MI300X
(MoRI-EP + MoRI-IO), written from this enablement. Documents, with measurements:
  - benchmarking method: ALWAYS discard the first post-boot run (cold Triton JIT made
    TTFT read 13.4s vs 906ms warm; with prefill eager the JIT cost lands in TTFT, not
    TPOT), and sanity-check against the HBM-bandwidth bound before blaming a kernel
  - the max_num_batched_tokens trap: a chunked-prefill SCHEDULER knob also sizes the
    MoRI EP dispatch buffer, so decode ran an 8192-token-wide all2all every step
    (302ms -> 88ms TPOT once sized for the real batch)
  - the DSA sentinel landmine: the invalid sparse-index sentinel must be 0, not -1,
    because aiter's mla_decode_fwd dereferences it (only bites at disagg long context)
  - three documented DEAD ENDS so they are not retried, including why mori's
    max_total_recv_tokens cannot decouple recv from send capacity (the clamp is a min())
  - cache/boot behaviour (three caches with different rules, the aiter baton lock,
    measured boot times), readiness signals for multi-node topologies, per-role env
    plumbing, and RDMA fabric verification (a node can be SLURM-'alloc' with a dead
    fabric - verify with ping/ib_write_bw before blaming code)

Co-Authored-By: Claude <noreply@anthropic.com>

* [GLM-5.1] Remove dead-end MoRI EP env plumbing and its misleading comments

Review cleanup. The VLLM_MORI_MAX_TOKENS_PER_RANK / VLLM_MORI_MAX_TOTAL_RECV_TOKENS
per-role plumbing was written while chasing the decode-TPOT bug and is NOT what fixed it
(the fix is `--max-num-batched-tokens 2048` on decode.dp). Worse, the comments asserted
that max_total_recv_tokens keeps recv capacity large enough for vLLM's profiling dummy
run -- which is false and was disproved by measurement: mori's

  MaxNumTokensToRecvPerRank() = min(ceil(maxTotalRecvTokens / worldSize),
                                    maxNumInpTokenPerRank)

is a min(), so maxTotalRecvTokens can only LOWER recv capacity, never raise it above the
send width. Anyone following those comments and setting the knobs would hit
"Total recv token overflow" at boot (observed at 512, 2048, and with recv=65536).

Removed: the per-role export block in moriio.sh, the stale models.yaml comment block, and
the six dead keys from _RECIPE_ENV_KEYS. Replaced with a short NOTE in moriio.sh pointing
at the real fix and at skills_vllm_disagg.md for the measurements and dead ends.

No functional change to the validated configuration: the knobs defaulted to 0/unset, so
the b8 runs never exercised them.

Co-Authored-By: Claude <noreply@anthropic.com>

* [GLM-5.1] Decode CUDA-graph FULL_AND_PIECEWISE: 2.1x (EP8) / 1.7x (EP16) faster decode

Second perf lever, on top of the decode --max-num-batched-tokens fix. Switching the decode
role from PIECEWISE to FULL_AND_PIECEWISE captures a FULL graph for the uniform-decode
shapes and falls back to PIECEWISE for the rest.

Measured on MI300X, ISL/OSL 1024/64, warm (first post-boot run discarded), image
rocmshared/pytorch-private:glm5.1-pr176-verify:

                        PIECEWISE   FULL_AND_PIECEWISE    gain
  1P/1D EP8  TPOT c8      88.0 ms        41.8 ms         2.11x
  1P/1D EP8  TPOT c16     91.5 ms        45.8 ms         2.00x
  2P/2D EP16 TPOT c8      94.1 ms        55.4 ms         1.70x
  2P/2D EP16 TPOT c16     96.2 ms        60.3 ms         1.60x
  1P/1D outTP c8        78.8 tok/s    143.7 tok/s        1.82x
  2P/2D outTP c8        66.7 tok/s    113.0 tok/s        1.69x
  2P/2D TTFT c16          2221 ms        1400 ms         1.59x
Both topologies now sit well below the published ~89 ms (EP8) / ~91 ms (EP16) reference.

Accuracy is unaffected. NIAH 2k-35k: 1P/1D 51/60, 2P/2D 55/60 - both inside the PIECEWISE
band (52-53 and 53 respectively; 2P/2D is in fact the best NIAH run of the campaign). No
length collapse, no '!!!', memfault=0, latencies equal-or-better at every length.

Caveats, stated rather than buried:
 - The gain SHRINKS as EP widens (2.11x at EP8 -> 1.70x at EP16). FULL captures the whole
   decode step including the cross-node all2all, which does not compress. This is validated
   at EP8 and EP16 ONLY; do not assume it holds at EP32 (which has a separate, unrelated
   correctness regression).
 - Capture costs ~92-94 s and ~3.0-3.5 GiB vs ~5 s / 7.2 GiB for PIECEWISE (fewer but
   larger graphs), so boot is longer. Irrelevant for a long-lived server, notable for CI.
 - Set DECODE_CUDAGRAPH_MODE=PIECEWISE to fall back.

Two code paths branch on CUDAGraphMode.FULL (sparse_attn_indexer.py:411 and the MoRIIO
READ-mode barrier); both are guards that skip host-side work under FULL, so enabling it is
safe here - the MoRIIO one is already a no-op in WRITE mode.

skills_vllm_disagg.md gains a section 2b with the table, the EP-width caveat, the capture
cost, and the observation that FULL is far less cold-JIT sensitive (its warmup run already
reads steady-state TPOT, where PIECEWISE's first run showed 13.7 s TTFT).

Co-Authored-By: Claude <noreply@anthropic.com>

* [GLM-5.1] Merge hygiene: drop lab leftovers, safe patcher defaults.

Rebase onto develop after ROCm#197 so the GLM card lives in
scripts/vllm_dissag/models.json. Remove the NFS silly-rename and
keepalive/NIAH-200k/skills lab files, revert DeepSeek-V3 context caps
and cluster-wide moriio.env NIC pins, turn persist-gate and DSA
sentinel patchers opt-in (they crash the v0.27 image), gate DSA on
GLM-5.*, and wire BENCHMARK_SCRIPT=niah for the 1P/1D smoke.

* [GLM-5.1] Point the recipe at the v0.27 image it was validated on.

The recipe named a pre-v0.27 image as "validated" and described patchers
that no longer apply. Serving GLM-5.1-FP8 on that older image returns
empty completions (NIAH 2k = 0/10), so name the image the Dockerfile
actually builds and state that the patchers are an old-image fallback.

* [GLM-5.1] Minimal MAD: drop runtime DSA patchers, make the image pin the contract

PR 206 shipped GLM-5.1-FP8 with nine apply_glm_*.py runtime monkeypatch scripts
(~1100 lines) invoked from the moriio connector. None of them run on the
production path: the validated model card sets GLM_SKIP_PATCHERS=1 because the
pinned vLLM (VLLM_REF=glm5.1-dsa-wideEP_on_vllm-v0.27) already carries every DSA
fix in-source. They were dead weight that MAD had to keep in sync with a fork.

This branch makes MAD carry catalog content only, and makes the image solely
responsible for vLLM source fixes:

- Delete all nine scripts/vllm_dissag/apply_glm_*.py.
- connector_runtime_patch() in connectors/moriio.sh is now a documented no-op for
  every model; _glm_dsa_runtime_patch() and the GLM MODEL_NAME branch are gone.
  Non-GLM behavior is unchanged (it already returned 0 before reaching any GLM
  code), so DeepSeek/Llama/gpt-oss are untouched.
- Remove the plumbing that only existed to feed the patchers: the
  GLM_SKIP_PATCHERS / GLM_PERSIST_GATE / GLM_DSA_SENTINEL_FIX docker -e forwards
  in run_xPyD_models.slurm, and GLM_SKIP_PATCHERS from the model card in
  models.json (with no patchers, "skip the patchers" is meaningless).
- Rewrite the models.yaml and Dockerfile comments that promised runtime patchers.
  The docs now state the actual contract: the image must carry the DSA fixes
  in-source and VLLM_REF is the requirement, not a preference.

Trade-off: this removes the fallback path for older images. On an image built
from a vLLM ref that predates the DSA fixes, GLM-5.1 now boots and then produces
garbage or stalls the disagg KV transfer, with nothing to recover it -- the fix
is to rebuild the image, not to patch at runtime. In exchange MAD stops carrying
a duplicate of upstream fork code that no supported configuration executes.
The Dockerfile, model card, models.yaml recipe, NIAH harness and slurm allowlist
are otherwise unchanged.

Verified without GPUs: tests/argv_assert.sh 25 passed / 0 failed; models.json
parses and the GLM card is intact; DRY_RUN argv for DeepSeek-V3,
DeepSeek-V3-5layer, DeepSeek-R1, amd-Llama-3.3-70B-Instruct-FP8-KV and
gpt-oss-120b is byte-identical to glm5.1-wideep-mad (so the only delta vs
origin/develop is the pre-existing one from that branch); GLM-5.1-FP8 still
emits --enable-expert-parallel, --block-size 1, and on decode
--max-num-batched-tokens 2048 + FULL_AND_PIECEWISE; bash -n clean.

* [GLM-5.1] Scope USE_INDUCTOR_GRAPH_PARTITION to GLM, restore other models' argv

The knob was introduced by the GLM v0.27 level-set (it does not exist on
origin/develop) with a default of 1, so every model served over the moriio
connector silently got "use_inductor_graph_partition":true appended to its
--compilation-config: DeepSeek-V3, DeepSeek-V3-5layer, DeepSeek-R1,
amd-Llama-3.3-70B-Instruct-FP8-KV and gpt-oss-120b. The flag is a fix for the
MLA custom-op boxing failure that only GLM-5.1 hits, so it belongs with the
model, not with the connector.

Flip the connector default to 0 and set USE_INDUCTOR_GRAPH_PARTITION=1 in the
GLM-5.1-FP8 models.yaml env: block, the same mechanism the recipe already uses
for VLLM_USE_LAYERNAME / KV_BLOCK_SIZE / DECODE_CUDAGRAPH_MODE. Add the key to
_RECIPE_ENV_KEYS so the image-baked < models.yaml < submit-time -e precedence
holds for it too (without it a submit-time override would lose to the recipe).

Verified offline (DRY_RUN argv, no GPUs): the five models above are now
byte-identical to origin/develop across moriio+rixl x wideEP+TP x
prefill+decode (40 cells, zero diff, no use_inductor_graph_partition anywhere),
GLM-5.1-FP8 prefill+decode argv is unchanged vs glm5.1-wideep-mad, and
argv_assert is 25 passed / 0 failed.

* [GLM-5.1] Fix false MORI_REF claim and stale image text in the GLM docs

Comment-only. Three claims in the GLM catalog text did not match reality:

1. connectors/moriio.sh said MORI_REF=42e895472b08 had the large-transfer
   notify/mapping fixes #424/#436/#432 "baked in". It does not: the pin is
   ROCm/mori#366 (2026-06-05), while #424 merged 2026-06-26 and #432/#436 on
   2026-06-29 — `gh api repos/ROCm/mori/compare/42e895472b08...<merge_sha>`
   reports status "ahead" for all three. The pin is left alone (the image was
   validated with exactly this MoRI); only the comment is corrected.

2. models.yaml said the unsupported rocmshared lab image "is only usable on a
   branch that still ships patchers" — a dangling reference from a branch with
   no patchers. It now names the one supported image outright.

3. models.yaml's long-context caveat described the old mori v1.2.1 lab image
   ("garbage beyond ~16-18k tokens") as if it applied to the pinned image, which
   contradicts both the glmv5.1 Dockerfile STATUS and the NIAH 2k-35k numbers in
   this same recipe. Re-scoped to history and pointed at the in-source fix
   (vLLM #47766). #40018 is also labelled as an issue, which it is.

Two audited citations needed no change: vLLM #47042 is only cited in
benchmark_niah.{py,sh} / README.MD / ARCHITECTURE.md, all of which are
origin/develop text that already calls it an issue (never a PR); and aiter#3921
(a gfx950 A8W8 change) was only cited by apply_glm_dsa_persistent_kernel_gate_fix.py,
which this branch already deleted.

* [GLM-5.1] Fix cross-model fallout: protect-list, fabric-IP default, patch mounts

Three defects that reached beyond GLM-5.1-FP8:

1. _RECIPE_ENV_KEYS listed only 18 of the 39 keys a models.yaml `env:` block can
   set. Once MODELS_YAML_PROTECT exists the driver protects ONLY listed keys, so a
   submit-time `-e KEY=...` silently lost to the recipe for the 14 unlisted non-GLM
   keys (Llama-405B/70B, DeepSeek family, gpt-oss-120b) and 7 GLM ones. That
   contradicts the precedence documented here, in models.yaml and in
   ARCHITECTURE.md. The list is now the exact union of models.yaml env: keys.

2. FABRIC_SUBNET defaulted to the lab prefix 10.158., changing which NIC every
   model advertises on any cluster that happens to have a 10.158.x address, and the
   slurm never forwarded it, so an operator override applied on the host but not
   inside the container (split-brain MASTER_ADDR vs host_ip). Now opt-in: unset =
   hostname -I's first entry, i.e. develop's behaviour, and the var is forwarded.

3. GLM_KERNEL_PATCH / GLM_BACKEND_PATCH bind-mounts overlaid vLLM sparse-MLA source
   from a host path. They do not exist on develop, nothing else in the tree reads
   them, and a host-path source overlay is precisely the runtime patching this
   branch removed in favour of the image pin. Dropped.

Also correct the `-e` -> `-f` rationale on the RDMA lib mounts: a dangling symlink
already fails -e; what -f excludes is a path that resolves to a non-regular file.

* [GLM-5.1] Correct false and stale claims in the GLM docs

Verified against ROCm/mori, vllm-project/vllm and the pinned vLLM fork with gh api:

- Dockerfile stage 1 claimed the MoRI pin is "v1.2.1 (the version for the 06_29
  mori121 image)". MORI_REF=42e895472b08 is ROCm/mori#366 (2026-06-05) and is 32
  commits BEHIND tag v1.2.1 (e31d426a, 2026-06-25). The text was copied from the
  base Dockerfile, which really does pin v1.2.1, and it also inverted models.yaml,
  which lists the mori121 image as NOT SUPPORTED.

- Dockerfile stage 3 claimed the vLLM pin is "the 06_29 validated Wide-EP WRITE-mode
  branch (matches the published mori121 image)". It is the GLM DSA v0.27 branch; the
  06_29 vLLM predates the in-source DSA fixes and cannot serve GLM-5.1-FP8 at all.

- VLLM_REF is documented as "(HEAD cda3648602) = upstream v0.27 tip + 7 commits".
  The branch tip is d723eb305 (+9); it advanced on 2026-08-15, before the MAD commit
  that measured the 3.4x decode speedup. Since VLLM_REF is a branch name, `docker
  build` resolves it to that day's tip. Record the real state and tell builders to
  pass an explicit --build-arg VLLM_REF=<sha> for an auditable rebuild.

- STATUS said "NIAH 2k-35k = 10/10" where the recipe records 51/60 and 55/60, and
  the vLLM pin note said "1P/1D + 2P/1D + 1P/2D ... decode PIECEWISE" where the
  validated topologies are 1P/1D EP8 + 2P/2D EP16 and decode is FULL_AND_PIECEWISE.

- The `docker build -f` example inside the glmv5.1 Dockerfile named the BASE
  Dockerfile.

- models.yaml's "3 recipe-defining ways" bullet still described decode as PIECEWISE
  with a ~3.7x ITL win after 638a033 moved it to FULL_AND_PIECEWISE. Reframed: the
  ~3.7x is PIECEWISE over eager, FULL_AND_PIECEWISE is the further 2.11x/1.70x
  already measured below.

- benchmark_xPyD.sh attributed "302ms vs ~89ms" to residual JIT absorbed by the
  first measured cell. models.yaml and moriio.sh attribute the same 302->88ms to
  the 8192-token-wide MoRI EP all2all buffer, fixed by --max-num-batched-tokens.
  Note the distinction rather than leaving two root causes for one number.

- vLLM #40018 is filed against gfx950 while this stack is gfx942; say so.

- README told users to build "the one fullsource Dockerfile" and never mentioned
  that GLM-5.1-FP8 needs the per-model glmv5.1 image the same README's table cites.

- benchmark_niah.sh passes every NIAH_* through explicitly except NIAH_SEEDS, which
  only worked by inheritance.

* [GLM-5.1] Sync the gate_check allowlist mirror with the slurm

The branch added GLM-5.1-FP8 to VALID_MODELS, MORI_EP_VALID_MODELS and
WIDE_EP_ONLY_MODELS in run_xPyD_models.slurm but not to gate_check.sh's copy, so
the new model's gate had no coverage. Add it, plus the four cases that pin the
intended envelope (moriio wideEP only; TP and DeepEP rejected).

The header also claimed the test "runs the REAL gate code path, so a future edit to
the lists/gate is caught" — it does not, it re-runs a hand-kept mirror, which is
exactly why this drift was possible. Say what it actually does.

* [GLM-5.1] Name the validated v0.27 image tag and the 0/10 evidence.

The IMAGE CONTRACT block said which ref the supported image is built from but
never named the published artifact, and gave no evidence for why older images
are unsupported. Name the tag (rocmshared/pytorch-private:glm5.1-vllm027-b8)
and record the measurement: the pre-v0.27 image
rocm/pytorch-private:glm-dockerimage-built-09072026 scored NIAH 2k 0/10 on the
1P/1D EP8 smoke, slurm job 216847.

This branch ships no runtime patchers, so an unsupported image has no fallback
and raises no error -- it serves and returns wrong output. Say that plainly.
The glmv5.1 Dockerfile gets the same tag and the same evidence so the two files
name one artifact; its vLLM stage already states that the mori121 image's vLLM
cannot serve GLM-5.1-FP8, which is what models.yaml says too.

Comment-only: no flags, no env, no pins move.

* [GLM-5.1] Pin VLLM_REF to the validated commit instead of a branch name

VLLM_REF defaulted to glm5.1-dsa-wideEP_on_vllm-v0.27, a mutable branch name, so
`docker build` resolved to whatever the tip was on the day of the build. The branch
has already moved once since validation (cda3648602 -> e8c186f71/d723eb305), which
means "the image is the contract" could not hold: two builds of the same Dockerfile
could ship different engines.

Pin the tip that every number in models.yaml was measured on, d723eb305e, and update
the places that described the ref as a branch (models.yaml, connectors/moriio.sh, the
Dockerfile header). Moving the pin now requires an explicit --build-arg and a
re-validation, which is the intent.

* [GLM-5.1] Correct the described lineage of the pinned vLLM commit

The header called dedbf6be8b the "upstream v0.27 tip". It is not: against
vllm-project/vllm, dedbf6be8b is a main-branch commit of 2026-08-09, 270 commits
ahead of the v0.27.0 tag and 17 behind releases/v0.27.0. Verified with the GitHub
compare API, which also confirms the pinned d723eb305e is that commit plus exactly
9 ROCm commits, 0 behind.

State what the commit actually is so a reader does not go looking for these fixes on
the v0.27 release branch.

* [GLM-5.1] Point VLLM_REF back at the branch, pending the mutable-ref fix

Reverts the SHA pin from the previous commit. The ref goes back to
glm5.1-dsa-wideEP_on_vllm-v0.27 so this Dockerfile stays consistent with how the
other vllm_dissag images name their vLLM ref; the mutable-branch problem is being
handled separately rather than solved one Dockerfile at a time.

The REPRODUCIBILITY note stays and now names the validated commit outright, so an
auditable rebuild is still one build arg away:
  --build-arg VLLM_REF=d723eb305eb78d1bda0ed357b2b54cc29487221f

The lineage correction from the previous commit is kept and reworded for the branch
form: the tip is dedbf6be8b plus 9 ROCm commits, and dedbf6be8b is upstream main, not
the v0.27 release.

* [GLM-5.1] Trim the connector_runtime_patch comment back to develop's

connector_runtime_patch is a return-0 no-op shared by every moriio model, and this
branch had grown its comment from 7 lines to 24.

The MoRI paragraph was wrong for the model that dominates this connector: it told the
reader their pin is 42e895472b08 and lacks ROCm/mori #424, #432 and #436, but
DeepSeek-V3 builds from vllm_disagg_inference.ubuntu.amd.Dockerfile with
MORI_REF=v1.2.1, 32 commits ahead of that sha and containing those fixes. The pin
story is already told, correctly scoped to the GLM image, in the glmv5.1 Dockerfile.

The GLM paragraph restated a rule the existing text already makes generic and
duplicated detail that belongs in the Dockerfile header and the models.yaml recipe.

Restores develop's wording plus one line pointing GLM readers at models.yaml, so the
hunk is a one-line diff against develop.

* [GLM-5.1] Drop the GLM line from connector_runtime_patch

The function is unedited by this branch, so it needs no note from it. Leaves
connector_runtime_patch byte-identical to develop; the GLM image contract is stated
in models.yaml and the glmv5.1 Dockerfile, which is where it belongs.

* [GLM-5.1] Correct the necessity claim for use_inductor_graph_partition

The comments in moriio.sh and models.yaml stated the flag was required for GLM,
citing "RuntimeError: unknown parameter type" on the first real MLA decode from the
stable-ABI concat_and_cache_mla boxed kernel failing to compose in the Dynamo-FX
partitioned graph.

That does not reproduce on the pinned image. A/B on 1P/1D EP8 (slurm 217352.91 with
the flag off vs .108 with it on) scored NIAH 2k 10/10 in both arms, with the emitted
--compilation-config verified per arm from the live engine cmdline, and zero
occurrences of that RuntimeError in any log. A matched 1024/1024 concurrency-8 sweep
put mean TPOT at 42.57 ms off vs 42.11 ms on, inside run-to-run noise.

Comments only; no behavior change. GLM keeps the flag ON because that is the
configuration every published number was measured with, which the comments now say
instead of claiming a correctness requirement.

* [GLM-5.1] Drop the models.yaml protect-list, restore develop's env precedence

_RECIPE_ENV_KEYS existed to add a third precedence tier so models.yaml could
override image-baked ENV while a submit-time -e still won. That tier only
matters if a supported image bakes a recipe key, and none does: docker inspect
of the glmv5.1 image shows no KV_BLOCK_SIZE, no VLLM_ROCM_USE_AITER_MLA and no
cudagraph settings -- its only ENV lines are build and toolchain vars. The case
it defended is a lab image baking DeepSeek tuning, already unsupported for GLM.

Against that it required a 35-key list in the slurm to be kept in sync with
every models.yaml env: block, in a different file, or the documented precedence
silently breaks for whichever key was missed.

vllm_disagg.sh's yaml-env block is now byte-identical to develop. Diff versus
develop drops from 79 to 44 lines in the slurm and 38 to 9 in the driver, the
remainder being the unrelated FABRIC_SUBNET fix.

Verified: DRY_RUN argv for GLM-5.1-FP8, DeepSeek-V3 and DeepSeek-R1 identical
before and after; GLM still emits use_inductor_graph_partition. Submit-time
overrides still win (USE_INDUCTOR_GRAPH_PARTITION=0 drops the key,
KV_BLOCK_SIZE=16 beats the recipe's 1) because an exported value is in
os.environ under develop's rule. tests/run_all.sh 25/25 plus the gate suite.

* [GLM-5.1] Make per-shape benchmark warmup opt-in, not default-on

benchmark_xPyD.sh is develop's default sweep path, shared by every model, so
gating per-shape warmup on ${SHAPE_WARMUP:-1} would have changed the measured
TPOT of recipes already validated without it (DeepSeek-V3/-R1, Llama-70B,
gpt-oss-120b). Default to off and let GLM-5.1-FP8 opt in from its models.yaml
env: block, which is the configuration its published latency numbers were
measured under. Other models' sweeps are now byte-identical to develop.

Also forward SHAPE_WARMUP and USE_INDUCTOR_GRAPH_PARTITION into the container
conditionally. docker run only passes variables named in an explicit -e, so
without this a submit-time export of either key never crosses the container
boundary and models.yaml applies unopposed -- meaning neither knob could be
A/B-tested from a wrapper script. Unset means not forwarded, so neither can
shadow a recipe on a normal run.

Covered by five new assertions in tests/argv_assert.sh, including that
GLM-5.1-FP8 is the only warmup opt-in.

* [GLM-5.1] Trim the comment bloat out of the GLM enablement diff

The GLM changes added 409 comment lines against 271 lines of code, a 60%
comment ratio that buried the actual recipe. models.yaml carried 130 comment
lines for one model entry where the DeepSeek entries use roughly 17, and
moriio.sh -- a file every model shares -- carried 25 comment lines for 6 lines
of code. This removes 131 of them without touching a single line of code.

Three kinds of comment are gone. Duplication: the MoRI EP buffer-width
rationale was written out in full in both moriio.sh and models.yaml, so it now
lives only at decode.dp where the setting actually is, and the same applies to
the inductor-partition and FABRIC_SUBNET explanations. Lab narrative: the
long-context collapse history, the unsupported-image inventory and the branch
archaeology described a superseded image rather than the shipped one. Internal
evidence: slurm step IDs, build labels and GitHub compare arithmetic that no
reader outside this lab can act on.

What stays is anything a future editor would break by removing the code it sits
on -- KV_BLOCK_SIZE=1 for the DSA indexer, the prefill cudagraph deadlock, the
sparse-indexer crash fix, the decode.dp TTFT floor -- plus the two safety
warnings that describe SILENT failures: unsupported images return confident
garbage rather than erroring, and long-context was fixed in-source so it must
not be chased with config flags. Measurements are kept only as one-liners where
they justify a non-obvious setting; the full tables moved to the work log.

The Dockerfile is trimmed against its sibling vllm_disagg_inference Dockerfile
rather than an arbitrary target: blocks copied from it are left byte-identical,
and only the GLM-specific additions are compressed.

Comment-only, proven three ways: the non-comment diff is empty, DRY_RUN argv is
byte-identical for GLM, DeepSeek-V3, DeepSeek-R1, Llama-70B and DSV3+DeepEP,
and the offline suites stay green at 24 gate + 30 argv assertions.

* [GLM-5.1] Correct the per-shape warmup rationale to match measurement

Both comments claimed the first measured cell of a shape absorbs residual JIT
and reports an inflated TPOT. An A/B on allocation 217352 at 1024/1024 con=8
does not support that: with the warmup on TPOT was 42.13 ms and with it off
42.05 ms, and the ~21 s P99 TTFT in the con=1 cell that follows the warmup is
present either way. State the real reason the recipe keeps it -- GLM's published
figures were measured with it on -- rather than a mechanism that was not
observed. Comments only; no code changes.

* [GLM-5.1] Keep the FULL_AND_PIECEWISE measurement table in models.yaml

The previous commit reduced this block to the two TPOT figures on the grounds
that the full table was PR-description material. That was premature: the
throughput column, the speedup multipliers and the NIAH accuracy row are the
baseline the upstream DSA PRs get verified against, and they need to be
readable next to the setting until those PRs merge.

Restores the two measured rows and the accuracy line, keeps the tightened
prose around them, and says in the comment that the rows are a temporary
verification baseline so a later reader knows they can go once upstream lands.

Comment-only: non-comment diff empty, DRY_RUN argv byte-identical for GLM,
DeepSeek-V3, DeepSeek-R1, Llama-70B and DSV3+DeepEP, offline suites green at
24 gate + 30 argv.

* [GLM-5.1] Drop the FABRIC_SUBNET opt-in; it fixes nothing by default

The launcher advertises hostname -I's first entry on both sides of the
container boundary, and socket_barrier binds --local-ip host_ip while peers
dial --node-ips IPADDRS. When that first entry is not routable between nodes
the barrier never completes and the job hangs at "Waiting for nodes" with no
error. That is a real defect on this cluster: job 211823 came up with
IPADDRS=10.224.2.61,10.224.0.194 and hung, and 211879 had one node on the
overlay and one on the fabric and hung with a single engine up. Both predate
GLM, so it is a cross-model launcher defect.

The mitigation carried here was an opt-in FABRIC_SUBNET prefix filter applied
to both lookups. Defaulting to unset, it fixes nothing for anyone: the next
time enumeration flips, the run hangs exactly as before because nobody exports
the variable. It was documented in no README, mentioned in no PR text and
covered by no test, which makes it dead code in an enablement PR. A real fix
means automatic routable-NIC selection, which changes NIC choice for every
model on every cluster and belongs in its own launcher PR with its own testing.

Reverting also removes a latent defect: _pick_fabric_ip had dropped develop's
NR==1 guard, so a multi-line srun stdout would have yielded a multi-line
MASTER_ADDR.

vllm_disagg.sh is now byte-identical to develop, so this PR no longer touches
it, and the slurm diff drops from 46 lines to 21. DRY_RUN argv is unchanged for
GLM-5.1-FP8, DeepSeek-V3 and Llama-70B; suites stay green at 24 gate + 30 argv;
no FABRIC_SUBNET reference remains in scripts/ or docker/.

* Trim the glmv5.1 Dockerfile header to the reviewed comment budget

Removes 14 comment lines of within-file duplication from the header,
bringing the Dockerfile to the 180-line comment target from review:

  - the docker build invocation appeared twice (header and "build
    context" block); keep the first, which also shows DOCKER_IMAGE_NAME
  - WITH_NIXL=0/=1 was explained in the header and again at the ARG;
    keep the ARG-site copy, leave the one-line mention in the connector
    summary
  - BASE_IMAGE override was stated in both STATUS and the closing block;
    fold the ~30-60 min compile time into STATUS and drop the block
  - the "add a vllm_disagg_inference.<model>.Dockerfile per future model"
    guidance duplicates README.MD

Also corrects a false claim inherited from the sibling Dockerfile: the
pin bullet said "validated recipe knobs baked as ENV", which contradicts
section 5 ("The image ships NO runtime recipe / tuning / platform ENV").
The recipe lives in models.yaml and is applied at launch.

Comment-only: the non-comment diff is empty, all 34 Dockerfile
directives are unchanged, and tests/run_all.sh is green (30 argv
assertions + gate checks).

* [GLM-5.1] Bump the component pins to the validated fresh stack

Moves the recipe off the older pin set onto the stack that was
re-validated end-to-end, and records the EP32 fix.

  MoRI    42e895472b08 -> 624002c897a3
  AITER   ROCm/aiter e03fa6040 -> raviguptaamd/aiter 624e43586b
  flydsl  >=0.1.7,<0.1.9 -> ==0.3.1
  vLLM    glm5.1-dsa-wideEP_on_vllm-v0.27 (branch) -> 094820b5d (pinned)
  router  ravgupta/discovery-dp-rank-roundrobin -> ravgupta/dp-roundrobin-on-tip

Why each:

* vLLM 094820b5d is branch glm5.1-dsa-wideEP_on_d626108b: the 10 GLM DSA
  commits rebased onto upstream d626108b (2026-08-20). It includes the MoRI
  combine() original-topk fix (623fdc946b), which resolves the EP32 token
  corruption the header previously called a KNOWN OPEN DEFECT. Measured at
  4P/4D EP32 on the pre-bump stack: NIAH 8k 0/10 -> 10/10, perf-neutral.
  Pinned to a sha rather than a branch so builds are reproducible.

* AITER: newer aiter claims native gfx942 support for gqa64 fp8 decode and
  routes GLM-5.1 to mla_a8w8_qh64_qseqlen1_gqaratio64_v3_ps, which GPU-faults.
  The 7-line fix lets gqa64 fall through to the capture-safe persistent
  view-fold, preserving cudagraph decode (FULL_AND_PIECEWISE) instead of
  forcing eager. Filed upstream as ROCm/aiter#4957. The fork ref is
  TEMPORARY -- revert AITER_REPO to ROCm/aiter once #4957 merges. This is
  what unblocks moving off the old e03fa6040 pin at all.

* MoRI 624002c897a3 and flydsl 0.3.1 are the co-requisites of that aiter
  bump (flydsl_fp8_mqa_logits is missing on 0.1.x).

Validation on the bumped pins: 1P/1D EP8 and 2P/2D EP16, prefill MoRI-HT
eager + decode MoRI-LL FULL_AND_PIECEWISE -- NIAH 2k 10/10, 8k 10/10;
8192/1024 con32 TPOT ~59 ms. 4P/4D EP32 has NOT been re-validated on these
pins (needs an 8-node allocation); the header says so explicitly rather
than implying coverage.

Also refreshes the header comments that described the old pins, so the
rationale text matches the ARGs.

Co-Authored-By: Claude <noreply@anthropic.com>

* [GLM-5.1] Fix two claims the pin bump left behind

Follow-up to 094d108, which bumped the pins but left three statements that
no longer match the file.

VLLM_REF is now a 40-char sha, so the REPRODUCIBILITY note contradicted
itself: it still warned that "this default is a BRANCH NAME, so it is
mutable" and then told the reader to pass --build-arg VLLM_REF=<the same
sha> for an auditable rebuild. The mutable-ref hazard is what the bump
removed, so say that instead.

That note also claimed the pin "is the tip every number in models.yaml was
measured on". It is not: the decode-cudagraph table in models.yaml was
measured on d723eb305e and has not been re-measured on 094820b5d. Left as
written it would credit the new stack with the old stack's numbers, which
is the same class of false-provenance claim this PR has already corrected
twice (e8a633d, 59f5fd8).

README.MD's model table still called EP32 "known-broken" while the
Dockerfile header two files away now says it is fixed. Matches the table to
the header's wording, including that EP32 has not been re-validated on the
bumped pins.

Comment- and doc-only: no Dockerfile directive changes, all ARG values
untouched.

* [GLM-5.1] Make 4P/4D EP32 launchable, pin the router, record the validation

Three fixes from validating an image built off this recipe unmodified
(glm5.1-pr206-9f16198, built from PR head 9f16198).

1. EP32 could not boot. GPU_MEMORY_UTILIZATION 0.8 OOMs during EngineCore
   init at 4P/4D ("Tried to allocate 6.00 GiB ... 157.95 GiB allocated"),
   which SIGTERMs every rank; the socket barrier then prints "Waiting for
   nodes" for ~30 min, so it reads as a hang rather than a crash. The MoRI
   dispatch/combine buffers scale with EP width but the fraction did not.
   The default is now derived from decode EP width: 0.70 at EP>=32, 0.8
   below. Measured: 0.70 boots (66.79 GiB KV cache) and holds through
   con=64; 2P/2D EP16 at 0.8 is unchanged (validated at stock in the same
   session). Submit-time -e still overrides.

   This had to go in the launcher, not models.yaml: the -e on the docker run
   line is unconditional, and vllm_disagg.sh skips any yaml key already in
   the environment, so the models.yaml GPU_MEMORY_UTILIZATION key never
   takes effect. Left in place (it documents intent) but the comment now
   says where the live value is. Note this is the only change here outside
   the glmv5.1 Dockerfile.

2. ROUTER_REF was a branch name, so two builds on different days could ship
   different routers - the same mutable-ref hazard the VLLM_REF sha pin
   removed. Pinned to 82dc9811. The comment above it still described the old
   PR ROCm#181 branch and its two commits; upstream has since merged the
   round-robin fix (prefill_dp_round_robin), so only the KV-notify fix is
   carried locally now.

3. STATUS said 4P/4D was fixed but not re-validated on these pins. It is now
   validated, so the claim is replaced with the measurements:

     NIAH, 3 seeds:  2P/2D 2k 9.7/10, 8k 9.7/10 | 4P/4D 2k 9.7/10, 8k 9.3/10
     8192/1024 con32 TPOT median: 2P/2D 60.0 ms | 4P/4D 70.4 ms
     0 failed requests across all 8 cells, 0 GPU faults

   The 9/10s are seed variance, not length collapse - across 12 cells every
   score is 9 or 10 with no pattern by topology or context length - so the
   text says ~93-97% retrieval rather than claiming a clean 10/10. Also
   records that EP32 costs ~17% TPOT over EP16, so nobody reads 4P/4D as a
   latency win. README.MD's model table matched to the same wording.

Co-Authored-By: Claude <noreply@anthropic.com>

* [GLM-5.1] Drop the quoted performance figures from the recipe

Comment-only. No ARG, no env value, no code line changes.

Reading this the way an outside reviewer would: the recipe quoted a lot of
specific latency and throughput numbers and none of them said which stack
produced them. That is a problem in both directions.

Some were measured on a stack this recipe no longer pins - the
decode-cudagraph table in models.yaml is from d723eb305e, as 9f16198 already
noted - so a reader would credit the current pins with older results. The
rest were real but were one configuration on one cluster, and a figure like
"TPOT 41.8 ms" sitting in a recipe file reads as a spec that CI or a customer
can hold you to. Neither belongs in a file whose job is to pin components.

Removed the numbers, kept the reasoning that made each setting the right
choice: FULL_AND_PIECEWISE is still justified (faster for decode, gain
narrows as EP widens, costs boot time), max-num-batched-tokens 2048 still
explains the TTFT-vs-latency tradeoff, and the inductor-partition A/B is
still recorded as neutral. Anyone can re-measure; nobody has to trust a
constant that may have gone stale.

The STATUS block keeps the NIAH results - those are a correctness claim and
are what the EP32 fix is actually about - and now states that a latency sweep
was run without quoting it, with a note to re-measure on your own hardware.

Also drops "1P/1D EP8" from the README model table. 1P/1D was validated on an
earlier stack, not on these pins; only 2P/2D EP16 and 4P/4D EP32 were. I
added that claim in error - it is the same false-provenance mistake 9f16198
corrected.

The EP32 OOM comment in the launcher keeps its error string, since that is
what someone chasing the "Waiting for nodes" hang will grep for, but loses
the KV-cache figure.

Co-Authored-By: Claude <noreply@anthropic.com>

* [GLM-5.1] Address Cemberk's 3 MAD-integration design issues

Issue 1 — NIAH is unschedulable and reports nothing:
- Add pyt_vllm_disagg_mori_glm-5.1-fp8_niah card with BENCHMARK_SCRIPT=niah
  and niah tag so it's CI-schedulable and tag-filterable.
- Add --niah mode to parse_to_csv.py: parses NIAH summary lines and emits
  madengine perf.csv rows (retrieval score per context length).
- benchmark_niah.sh now calls parse_to_csv.py after the harness run.

Issues 2 & 3 — GPU_MEMORY_UTILIZATION precedence fix:
- Fix the broken yaml path: slurm now conditionally forwards
  GPU_MEMORY_UTILIZATION only if set by submit-time, and always passes
  _GPU_MEM_UTIL_LAUNCHER_DEFAULT as a separate var.
- vllm_disagg.sh applies the launcher fallback after yaml loads, so:
    submit-time -e  >  models.yaml  >  launcher fallback (0.70@EP>=32, 0.8)
- Remove inert GPU_MEMORY_UTILIZATION from GLM and Qwen yaml (they now
  use the topology-aware fallback).
- Update header comment and README to document the precedence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: raviguptaamd <ravi.gupta@amd.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ravi Gupta <raviguptaamd@users.noreply.github.com>
* DSv4 max-num-batched-tokens 8192

VLLM now defaults to 16384; too much at 1m context

* Remove space

---------

Co-authored-by: Rahul Garg <rahul.garg@amd.com>
* sglang_disagg: add agentic replay benchmark as a recipe-native option

Adds aiperf-driven agentic trace-replay (inferencex-agentx-mvp) as a
first-class benchmark for the sglang_disagg recipe, selectable via
BENCHMARK_SCRIPT_FILE=benchmark_agentic.sh (or the AGENTIC=1 shorthand)
and launched through the standard `sbatch run_xPyD_models.slurm`
entrypoint. Recipe-only: no GitHub Actions / CI workflow dependency.

Harness (new):
- scripts/common/agentic_lib.sh          (aiperf driver; passes --server-metrics)
- scripts/common/validate_agentic_result.sh
- scripts/common/preflight_weights.sh
- scripts/common/verify_agentic_image.sh
- scripts/sglang_disagg/benchmark_agentic.sh

Launcher (gated behind agentic selection; default sweep unchanged):
- sglang_disagg_mori_io_ep.sh: --enable-metrics --enable-metrics-for-all-schedulers,
  strip --disable-radix-cache, export AGENTIC_SERVER_METRICS + curl probe
- run_xPyD_models.slurm: 10.158.x IP preference, agentic/metrics env passthrough,
  AGENTIC=1 shorthand
- mori_ep_env.sh: MORI_IB_GID_INDEX=3, NCCL_IB_GID_INDEX=3

Docs:
- README.MD: Agentic Replay Benchmark section (command, env knobs, supported
  models/modes, outputs, validation)
- models.yaml: note on launcher-gated radix-cache strip
- salloc_launch.sh: agentic sample commands

Verified via cluster run 207554 (1P/1D DeepSeek-V3): server_metrics.present=true,
gpu_cache_hit_rate=32.4%, error_rate ~2.9% (PASS).

* vllm_dissag: add agentic replay benchmark as a recipe-native option

Port the sglang agentic trace-replay feature to the vLLM disaggregated P/D
launcher, reusing the endpoint-agnostic shared harness (scripts/common/*):

- benchmark_agentic.sh: aiperf agentic hook, AGENTIC_PORT resolved from the
  vLLM router port (BENCHMARK_PORT, default 30000); finds agentic_lib.sh via
  NIXL_COOKBOOK_PATH/NIXL_REPO_DIR/_here ../common candidates.
- run_xPyD_models.slurm: AGENTIC=1 shorthand + agentic BENCHMARK_SCRIPT case;
  forward agentic env (AGENTIC_CONC, DURATION, AGENTIC_PORT, MODEL_PREFIX,
  ENABLE_PREFIX_CACHE, ENABLE_SERVER_METRICS, DRY_RUN, ...) and NIXL_REPO_DIR
  so the in-container hook resolves common/.
- vllm_disagg.sh: export AGENTIC_PORT=BENCHMARK_PORT before the hook exec.
- connectors/moriio.sh, rixl.sh: on the agentic path strip the hardcoded
  --no-enable-prefix-caching (prefix cache ON) and export AGENTIC_SERVER_METRICS
  at the backend serve ports; gated so the default sweep argv is unchanged.
- models.yaml: note prefix caching is re-enabled via the connector strip.
- common/verify_agentic_image.sh: add a vLLM branch (AGENTIC_ENGINE=vllm) that
  checks the KV transfer backend import + vLLM OpenAI API server module.

* vllm_dissag: add agentic_models_shim for /v1/models under MoRIIO discovery

The vLLM PD router returns 503 on /v1/models in MoRIIO discovery mode
(empty HTTP worker registry), but the shared agentic_lib.sh gates router
readiness + served-model resolution on /v1/models. Add a lightweight shim
that answers /v1/models (gated on the router's /health) and stream-proxies
all other routes to the PD router; AGENTIC_PORT points at the shim. Wired
into vllm_disagg.sh on the agentic path only (default sweep unchanged).

* agentx: add config-driven multi-workload agentic benchmarking suite

Unify regenerated (self-contained) and HF-downloaded trace sources behind one
config-driven harness that replays both through aiperf's inferencex-agentx-mvp
scenario against a single disaggregated endpoint.

- scripts/common/agentx/: generic seed-deterministic weka_trace corpus
  generator (gen_agentx_profile.py), profile conformance verifier
  (verify_agentx_profile.py, 13-axis), and a PyYAML-optional config/profile
  loader (agentx_config.py). Ships agentic.example.yaml + caseA/caseB/custom/
  small profiles.
- scripts/common/benchmark_agentic_suite.sh: iterate a workloads list against
  one served endpoint; per workload materialize+verify (source=profile) or
  resolve --public-dataset (source=hf), run a context-compat check, sweep
  concurrency, and write per-workload result dirs + a combined suite_summary.json.
- agentic_lib.sh: add materialize_corpus() and context_compat_check(); branch
  resolve_trace_loader() for the profile source; per-workload knobs in
  build_replay_cmd() (trajectory ratios, --max-context-length incl. Case-B
  524288, num-dataset-entries); honor MODEL=auto and guard it under set -u.
- sglang_disagg/vllm_dissag benchmark_agentic.sh route through the suite driver
  (AGENTIC_CONFIG); run_xPyD_models.slurm forward AGENTIC_CONFIG/AGENTIC_WORKLOAD/
  SUITE_CORPUS_DIR/MAX_MODEL_LEN into the container.

Validated end-to-end on MI300X with Qwen3-0.6B: verifier 13/13 (PyYAML and
forced-fallback, byte-identical corpus) and a suite run at 0.0% error rate.

* agentx: rename conformance presets caseA/caseB -> conformance_256k/conformance_512k

Rename the two ExplainX/ROCm#173 conformance workload presets to role-based names:
  profiles/caseA.yaml -> profiles/conformance_256k.yaml
  profiles/caseB.yaml -> profiles/conformance_512k.yaml

Updates the presets' name: field and all live preset references (agentic.example.yaml
workload name:/preset:, agentx_config.py and gen/verify docstrings).

The id_prefix session-id salt is intentionally preserved as "caseA" in both profiles
(and DEFAULT_ID_PREFIX in gen_agentx_profile.py) to reproduce ROCm/MAD ROCm#173 byte-for-byte.
Genuine ROCm#173 historical filename references (gen_caseA_conformance.py, verify_caseB.py, etc.)
are kept as provenance.

Also bundles the related, in-progress agentic-suite work: preset-chain merge + Tier 1/
Tier 2 corpus filtering (filter_weka_corpus.py, inferencex_256k.yaml), HF loader context
gating, and suite driver/lib support.

* agentic-suite: remove scripts/vllm_dissag/ (moved to local branch miali/vllm-disagg-agentic)

* agentx: add core benchmarking docs (README, profiles authoring guide, scenarios cookbook)

documents the config schema, profile/preset authoring + verify pre-gate, and a scenarios cookbook; docs-only, no code changes.

* agentx docs: add run-timing notes, verifier output sample, and concurrency-sweep clarification

* docs(sglang_disagg): add agentic-suite invocation examples and forward suite env vars

Add 5 copy-paste agentic-suite invocation examples, an env-var table, and
cross-links to the agentx core docs in the sglang_disagg README. Forward
SUITE_CORPUS_FORCE, AGENTIC_STRICT_CONTEXT, and AGENTIC_HF_ISL_TAIL into the
container from run_xPyD_models.slurm so the documented knobs take effect.

* Restore scripts/vllm_dissag/ to match develop

Revert the removal from 99faef3; the vLLM disaggregated launcher must stay
in sync with develop on this branch.

* vllm_dissag: add agentic-suite replay benchmark as recipe-native option

Mirror the sglang_disagg agentic integration into the vLLM disaggregated
launcher so AGENTIC=1 runs the multi-workload AgentX suite. Adds the
benchmark_agentic.sh hook (AGENTIC_PORT resolves to the vLLM proxy
BENCHMARK_PORT, with suite routing when AGENTIC_CONFIG/AGENTIC_WORKLOAD is
set and DRY_RUN support), the agentic_models_shim.py for /v1/models under
MoRIIO discovery, connector metrics export + prefix-cache handling for
moriio/rixl, suite env-var forwarding in run_xPyD_models.slurm, and README
invocation examples cross-linked to the agentx docs.

* vllm_dissag: SSE-safe agentic shim + configurable rixl+TP barrier port

1. agentic_models_shim.py _proxy: force Content-Type: text/event-stream for
   streaming relays (request "stream": true or SSE upstream) so aiperf strips
   the SSE framing instead of marking every request invalid.
2. rixl.sh + run_xPyD_models.slurm + tests/run_interactive.sh: make the rixl+TP
   container barrier port env-overridable (BARRIER_PORT, default 5000),
   forwarded into the container and used by the host-side fuser cleanup.

* docs(agentx): document all suite env vars and minimal required set

Add a grouped environment variable reference covering previously
undocumented user-facing knobs (context, warmup, HF/corpus, timing),
add explicit minimal-required commands for the AGENTIC_CONFIG suite and
config-less AGENTIC_WORKLOAD entry points, and split the misleading
AGENTIC_WORKLOAD example into config-less vs config-filter variants.

* docs(agentx): add integration template hook and close onboarding gaps

Add scripts/common/agentx/templates/ with a copy-paste benchmark_agentic
hook template and an integration guide so any backend (single-node,
disaggregated, or other framework) can wire up the agentic suite by
filling a few marked fields. Also make the core README Quick start
runnable and close remaining onboarding gaps (serve-endpoint
prerequisite, accurate RESULT_DIR default, qualified slurm path).

* disagg: auto-resolve served max_model_len from real P/D worker

In disaggregated serving the front-end routers (sglang_router :2322,
vLLM agentic_models_shim) do not advertise max_model_len, so the agentic
benchmark's context auto-detect returned 0 and ran with an unvalidated
window (operators had to pin MAX_MODEL_LEN by hand). Each backend hook now
queries the real prefill worker (first host:port in AGENTIC_SERVER_METRICS):
- vLLM: worker /v1/models ModelCard.max_model_len
- SGLang: worker /v1/models, falling back to /get_server_info
On success it exports MAX_MODEL_LEN so it flows into both the suite and
single-workload paths; if a worker is set but unresolvable it fails fast
(no silent default). Non-disagg (no AGENTIC_SERVER_METRICS) and DRY_RUN
fall through to the existing behavior unchanged.

Adds node-free offline tests (stdlib HTTP stub) covering resolve,
fail-fast, non-disagg fallthrough, and propagation into the suite plan.

* docs(agentx): document disagg context resolution and sync template

Close disagg doc/template drift from the served-context-window work:
add the disaggregated worker-probe block and AGENTIC_RESOLVE_ONLY to the
integration template, document AGENTIC_RESOLVE_ONLY and the dual role of
AGENTIC_SERVER_METRICS (also probed for max_model_len on disagg), and
correct the front-end /v1/models auto-detect wording in the sglang and
vLLM launcher READMEs.

* agentx: add source:corpus replay and configurable aiperf scenario

Two suite configurability additions (both default to the existing behavior
when unused, so the legacy single-workload path is byte-identical):

- source: corpus  replays an existing on-disk weka_trace corpus directory
  as-is via --input-file (no generate, no download). Verification is
  optional: it runs the profile pre-gate only when a preset/inline profile
  is supplied. ISL-tail resolution: explicit isl_tail: > profile-derived >
  1048576 (the gate caps to the served window, so it only over-WARNs).
  Trajectory/num-dataset-entries follow the real-trace (hf) path.

- run.scenario makes the aiperf --scenario configurable (default
  inferencex-agentx-mvp), overridable by AGENTIC_SCENARIO env or a
  per-workload scenario: key (re-derived each iteration so it can't leak).
  The workloads: list is unchanged.

Docs (agentic.example.yaml, SCENARIOS.md) updated accordingly.

* docs(agentx): add HOW_IT_WORKS explainer and onboarding section

Add HOW_IT_WORKS.md: a toy-example walkthrough of the trace-replay
mechanism (content-independence, seed-deterministic corpus generation,
multi-turn prefix reuse via hash_ids, theoretical vs GPU cache-hit) with
mermaid diagrams, mapped back to the conformance_256k profile. Also adds
an "Onboarding new users" section to the agentx README (reading path,
DRY_RUN preview, AGENTIC_WORKLOAD=small smoke run). Committing the doc
also resolves the README's existing See-also link to it.

* docs(agentx): cross-link launchers to onboarding; fix content-independence

Add reciprocal "start here" links from the sglang and vLLM launcher
READMEs into the AgentX core README/SCENARIOS/HOW_IT_WORKS, and extend
the onboarding path to name both launchers (not just sglang). Correct
HOW_IT_WORKS.md Section 2: input/prompt text is synthesized client-side
by aiperf's PromptGenerator keyed by hash_ids (shared ids -> identical
blocks -> cache hits); only output text is server-produced (measured,
then discarded). Diagram updated to match.

* fix(agentx): honor config concurrency/duration; log WARN verdict as WARN

The backend hooks (sglang/vllm benchmark_agentic.sh) and the suite driver
injected AGENTIC_CONC=16 / DURATION=120 (and 900) into the already-exported
env before agentx_config.py runs, so the loader's env-override layer always
clobbered the YAML run.concurrency/duration. Remove those default injections
so precedence is: explicit env > config file > loader defaults (16/900).
The legacy single-workload path is unchanged: build_replay_cmd still
self-defaults via ${AGENTIC_CONC:-16}/${DURATION:-120}.

Also add agentic_warn() and use it for the context clamp-and-run notice
(CONTEXT_VERDICT=WARN), which previously printed an [agentic][ERROR] prefix
despite being a warn+clamp+run, not a failure.

Includes a co-located fix in resolve_served_max_model_len: the /server_info
fallback now reads context_length only (not max_total_num_tokens, which is
the KV-cache token-pool budget, not the per-request context window).

* fix(agentx): close file handles, guard required fields + CLI arg values

Address 3 validated CRITICAL review findings in the AgentX helper scripts:

- Resource leaks: wrap json.dump/json.load in `with open(...)` in
  gen_agentx_profile.py (per-session write loop) and
  verify_agentx_profile.py (per-corpus read loop).
- Missing required-field handling: emit a clear error (instead of a raw
  KeyError traceback) when a profile omits required distribution fields
  in gen_agentx_profile.py and verify_agentx_profile.py, and when
  agentx_config._isl_tail() has neither clamps.isl nor isl_p.
- Hand-rolled arg loops: add a shared _argval() guard so a flag given
  without its value exits with a usage message instead of StopIteration,
  in gen_agentx_profile.py, verify_agentx_profile.py, filter_weka_corpus.py,
  and agentx_config.py.

Issue ROCm#4 (walrus `:=` in vllm_dissag/benchmark_parser.py) is a FALSE
POSITIVE: the codebase targets Python 3.8+ (runtime 3.10; pandas), so no
version guard is needed. Happy path unchanged; offline suite 30/30.

* fix(agentic): harden launcher IP resolution, MoRI preflight, weight check, uv install

- A: run_xPyD_models.slurm derives MASTER_ADDR/node IP by preferring the
  10.158.x fabric IP (unchanged on OCI), falling back to the first hostname -I
  address, and failing fast with a clear error when none resolve (was: empty
  value -> barrier hang). Applied at both sites.
- C: verify_agentic_image.sh skips the Mooncake import check when RUN_MORI=1
  (MoRI IO backend does not require Mooncake), keeping it for the default path.
- F: preflight_weights.sh captures srun exit status and treats a nonzero exit
  or empty output as a hard failure instead of silently passing.
- D: agentic_lib.sh ensure_agentic_uv prefers an on-PATH uv, pins the uv
  version, and gates the remote installer behind AGENTIC_ALLOW_UV_INSTALL=1.
- B: gen_agentx_profile.py documents that floor division for block count is
  intentional for byte-for-byte reproducibility (no arithmetic change).

* fix(agentic): forward AGENTIC_ALLOW_UV_INSTALL/AGENTIC_UV_VERSION into container

The ISSUE D uv-install opt-in gate lives in agentic_lib.sh, which runs inside
the disagg docker container. Env vars only reach that container through the
launcher's explicit -e allowlist, so add the two new uv knobs (defaulting
empty -> gate stays safe-by-default) to keep cluster runs able to install/use
uv when AGENTIC_ALLOW_UV_INSTALL=1 is set.

* test(agentx): add offline test + cluster submit/harvest harness

run_offline.sh: node-free suite covering config resolution, corpus
gen/verify, DRY_RUN plans, source:corpus, and scenario overrides.
submit_matrix.sh / harvest.sh: sbatch matrix launcher and results
harvester for the disagg agentic benchmark cells.

* fix(agentic): default AGENTIC_ALLOW_UV_INSTALL=1 on the direct agentic path

The agentic benchmark hooks are an explicit opt-in entrypoint, so default the
pinned uv install to on (overridable with AGENTIC_ALLOW_UV_INSTALL=0) instead of
making developers set it manually. Non-agentic launcher paths keep the gate off.

* fix(agentic): forward HF_HOME conditionally in sglang launcher

Use ${HF_HOME:+-e HF_HOME=$HF_HOME} so HF_HOME is only passed into the
container when set on the host, matching the vLLM sibling launcher. The
previous unconditional -e HF_HOME="${HF_HOME:-}" injected an empty
HF_HOME on the default (non-agentic) sweep, which huggingface_hub reads
as present-but-empty and resolves HF_HUB_CACHE to a relative hub/ under
CWD. Restores byte-for-byte behavior for non-agentic sweeps.

* fix(agentic): forward optional env conditionally in sglang launcher

Convert the empty-default env-forwarding lines (-e VAR="${VAR:-}") in the
per-node docker run block to the conditional idiom ${VAR:+-e VAR=$VAR}, so
an unset host var no longer injects an empty string into the container (the
same class of bug already fixed for HF_HOME in 6a8f32b). This matches the
vLLM sibling launcher scripts/vllm_dissag/run_xPyD_models.slurm.

Lines with a meaningful default (DOCKER_IMAGE_NAME, USE_CX7_NICS,
BENCHMARK_COMBINATIONS) and always-set required vars are left unchanged.

Addresses Cemberk's review item ROCm#3 on ROCm/MAD PR ROCm#213.

* fix(agentic): make fabric subnet prefix configurable in launcher

The sglang disagg launcher hardcoded a 10.158.x fabric-subnet preference
when resolving MASTER_ADDR / node IPs from multi-homed `hostname -I` output.
That embeds one cluster's (OCI useocpm2m) addressing repo-wide and silently
changes node coordination for every sweep on other clusters.

Introduce FABRIC_SUBNET_PREFIX (default "10.158.") near the node-info
section and reuse an escaped, start-anchored regex (FABRIC_SUBNET_RE) at
both the MASTER_ADDR and per-node IP selection sites. The default keeps OCI
behavior byte-for-byte unchanged when unset; an empty prefix skips the
prefix match and falls straight through to the first-address fallback. The
existing first-address fallback and fail-fast-on-empty logic are preserved.

Addresses Cemberk's review item ROCm#4 on ROCm/MAD PR ROCm#213.

* feat(agentic): declare 4 agentic models.json entries; gate on mode; remove AGENTIC=1

Replace the implicit "AGENTIC=1 layered over every model" mode with declared,
agentic-tagged models.json entries and mode-based gating, addressing Cemberk's
PR ROCm#213 (ROCm/MAD) headline feedback. The default sweep path is byte-for-byte
unchanged.

- Add 4 validated, `agentic`-tagged models.json entries (BENCHMARK_SCRIPT=agentic):
    * pyt_sglang_disagg_mori_io_agentic_qwen3-32b   (AGENTIC_WORKLOAD=conformance_256k)
    * pyt_sglang_disagg_mori_dp_agentic_deepseek-v3 (DP_MODE=1, AGENTIC_CONFIG suite)
    * pyt_vllm_disagg_mori_agentic_deepseek-v3      (AGENTIC_CONFIG suite)
    * pyt_vllm_disagg_mori_agentic_deepseek-v3-5layer (AGENTIC_CONFIG suite)
- SGLang launcher: add a BENCHMARK_SCRIPT case selector (sweep|agentic) mirroring
  vLLM; sweep -> benchmark_xPyD.sh (default preserved), agentic -> benchmark_agentic.sh.
- Hard-remove the redundant AGENTIC=1 shim from both launchers (breaking change).
- Re-gate shared code on the mode (BENCHMARK_SCRIPT==agentic) instead of the
  resolved filename (sglang_disagg_mori_io_ep.sh, connectors/rixl.sh,
  connectors/moriio.sh, vllm_disagg.sh); execution paths keep BENCHMARK_SCRIPT_FILE.
- Forward BENCHMARK_SCRIPT into both containers so in-container gates resolve.
- models.yaml + READMEs: note the cache-flag strip is driven by BENCHMARK_SCRIPT=agentic
  and exercised only by agentic-tagged entries; add a validated-configs table, a
  `madengine run --tags agentic` discovery note, and a Breaking Change note.

* chore(agentic): finish AGENTIC=1 removal in submit_matrix + stale docs

Follow-up to the mode-based agentic gating. AGENTIC=1 was hard-removed from the
launchers, so complete the removal in the remaining references:

- submit_matrix.sh: functional fix — the campaign driver's per-cell env array used
  AGENTIC=1, which would now submit in SWEEP mode; switch to BENCHMARK_SCRIPT=agentic.
- salloc_launch.sh: commented example recipes AGENTIC=1 -> BENCHMARK_SCRIPT=agentic.
- benchmark_agentic.sh (vllm) + AgentX templates: reword header comments off the
  removed shorthand.
- READMEs: reword the Breaking Change note off the literal AGENTIC=1 token.

No AGENTIC=1 references remain (grep -rn 'AGENTIC=1' scripts/ is empty); default
sweep path unchanged.

* refactor(agentx): unify agentic hooks into shared --backend script

Collapse the two byte-identical-except-three-axes agentic benchmark hooks
(scripts/sglang_disagg/benchmark_agentic.sh, scripts/vllm_dissag/benchmark_agentic.sh)
and the stale copy-paste template
(scripts/common/agentx/templates/benchmark_agentic.template.sh) into ONE shared,
--backend-parameterized source of truth at scripts/common/benchmark_agentic.sh
plus two ~12-line shims that exec it with --backend sglang|vllm.

The three per-backend axes now live in a single `case "$backend"`:
  1. AGENTIC_PORT default (sglang 2322; vllm the BENCHMARK_PORT/PROXY_PORT/8000 cascade).
  2. ctx-window resolver endpoint list (`ctx_endpoints=("path|kind" ...)`): sglang
     probes /v1/models then /get_server_info; vllm probes only /v1/models. The
     shared script reuses the sglang two-endpoint python parser (kind-driven) for
     both, looping the list.
  3. lib-locator repo-dir candidates: a superset covering MOONCAKE_REPO_DIR,
     NIXL_COOKBOOK_PATH and NIXL_REPO_DIR (harmless when unset).

Drift-proofing: the resolver, suite/DRY_RUN dispatch, and the 5-call replay tail
now exist once, so future edits can no longer diverge between backends. Adding a
backend is a case arm + a shim instead of copying a template (docs updated in
templates/README.md). Net ~410 deletions vs ~206 insertions.

Verified offline (no GPU): bash -n on all three; run_offline 31/31 PASS; both
resolve_ctx_offline suites ALL PASS (sglang exercises the two-endpoint arm through
the shim); per-backend port/endpoint parity confirmed.

Cemberk PR ROCm#213 review item ROCm#7.

* docs(agentx): consolidate SCENARIOS into README; trim launcher env duplication (Cemberk ROCm#9)

- Fold all 10 SCENARIOS.md recipes into README.md as a new ## Examples section
  (Option A full fold); de-duplicate Scenario 1 and Scenario 9 against the
  existing Quick start / Preview sections via cross-links.
- Delete scripts/common/agentx/SCENARIOS.md.
- Retarget all cross-references (11 sites) to README.md#examples anchors.
- Trim the duplicated AgentX env tables in the sglang/vllm launcher READMEs to a
  pointer at the central env reference plus launcher-specific rows only.

* refactor(agentx): dedup per-backend ctx-resolve stub + offline test into common (Cemberk ROCm#8)

* docs(agentx): add verify_agentx_profile.py example, container-path troubleshooting, and error reference

Add a cookbook example showing the agentx_config.py --emit-json ->
verify_agentx_profile.py flow (success + failure output), a troubleshooting
entry for AGENTIC_CONFIG paths not visible inside the container bind mounts,
and a consolidated symptom -> cause -> fix error reference table.

* fix(agentx): resolve N1 parser + robustness guards

- N1: Parse same-indent block sequences in fallback YAML loader
- Gen guards: block_size>=1, turns non-empty/length-match/positive-weights
- Verify guard: missing 'requests' in session JSON
- Preset guard: file-not-found check before load

Tests: add offline-gate assertions (fallback parser + guards)

* fix(agentx): resolve shell driver failure masking

- Suite driver: replace \`|| true\` with _suite_failed propagation
- SGLang launcher: add set -o pipefail before tee
- SGLang launcher: expand \$HOME in AGENTIC_CONFIG before docker forward

Tests: add static guards (|| true removal, pipefail, \$HOME expansion)

* fix(agentx): resolve blocking issues from Cemberk PR ROCm#213 2nd review

Address two blocking issues preventing merge:

1. vLLM launcher /home/miali expansion
   - Add AGENTIC_CONFIG expansion before container forward (matches SGLang)
   - Use  pattern (safer than eval)
   - Extend offline test to cover vLLM launcher

2. Example config references in models.json
   - Change 4 entries to use AGENTIC_WORKLOAD shorthand
   - Prevents 4.75h runs pointing at demo config
   - Comment out my_corpus/my_case in agentic.example.yaml
   - Add test_offline.yaml for offline tests (separate from user example)
   - Fix continue-on-failure for missing input_dir

Testing:
- Offline tests: 34/34 pass
- JSON/YAML syntax: valid
- DRY_RUN: verified with conformance_256k

Resolves: Cemberk blocking issues ROCm#1 and ROCm#2
Ready for re-review

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(vllm_dissag): add niah to BENCHMARK_SCRIPT valid-list error message

Merge resolution kept only the agentic variant of the invalid-value
message; niah is a working case arm and belongs in the valid list.

---------

Co-authored-by: Mir Ali <miali@useocpslog-002.amd.com>
Co-authored-by: MIR-AMD <mir@amd.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: MIR-AMD <mir@mir-amd.local>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It’s a large sync touching many launchers, Dockerfiles, and model/discovery paths, and it needs manual validation of the affected workflows (Primus/JAX discovery, disagg agentic paths, and CI entrypoints).

Pull request overview

Syncs mad-rccl with develop while adding AgentX/agentic replay support for disaggregated backends, improving Primus JAX (MaxText/MaxDiffusion) integration, and addressing a MoRI overlay Docker build failure at the pinned commit.

Changes:

  • Extend vLLM + SGLang disaggregated launchers/configs to support agentic trace replay (metrics endpoints, readiness workarounds, cache/prefix gating) and add related harness utilities/tests.
  • Add Primus fetch helper plus new JAX/Primus docker images and model discovery for MaxText and MaxDiffusion; refactor/retire older JAX-MaxText benchmark scripts.
  • Patch MoRI overlay Docker build to guard unconditionally-built UMBP tests that fail in GPU-less docker build environments.
File summaries
File Description
tools/run_models.py Allows per-model dockercontext, normalizes Docker image/container names, and avoids sys.exit() from deep call sites.
tools/fetch_primus.sh Adds an idempotent host-side Primus checkout helper for discovery and Docker build context.
scripts/vllm/configs/extended.yaml Adds expert-parallel enablement for a serving config and fixes YAML indentation.
scripts/vllm/configs/default.yaml Adds max batched tokens knob and fixes YAML indentation.
scripts/vllm_dissag/vllm_disagg.sh Adds agentic shim support and ensures GPU memory util defaulting is launcher/yaml-friendly.
scripts/vllm_dissag/tests/run_interactive.sh Makes barrier-port cleanup respect BARRIER_PORT override.
scripts/vllm_dissag/tests/gate_check.sh Updates the gate-check harness commentary and adds GLM-5.1-FP8 cases.
scripts/vllm_dissag/tests/argv_assert.sh Adds assertions that per-shape warmup remains opt-in and overrides are forwarded.
scripts/vllm_dissag/parse_to_csv.py Adds NIAH log parsing and optional perf.csv emission for retrieval benchmarks.
scripts/vllm_dissag/models.json Adds GLM-5.1-FP8 entries plus agentic DeepSeek-V3 entries (and a NIAH variant).
scripts/vllm_dissag/connectors/rixl.sh Adds barrier-port override support and agentic gating for prefix cache + metrics.
scripts/vllm_dissag/connectors/moriio.sh Adds IGP opt-in, agentic gating for prefix cache + metrics, and includes model args consistently.
scripts/vllm_dissag/benchmark_xPyD.sh Adds opt-in per-shape warmup to stabilize per-shape kernels/cudagraphs.
scripts/vllm_dissag/benchmark_niah.sh Replaces blind sleep with readiness polling, adds seeds/warmup, and emits perf.csv via parser.
scripts/vllm_dissag/benchmark_agentic.sh Adds a thin shim to the shared agentic benchmark runner for vLLM.
scripts/sglang_disagg/sglang_disagg_mori_io_ep.sh Adds agentic gating for radix cache + metrics and generalizes benchmark script selection.
scripts/sglang_disagg/salloc_launch.sh Documents agentic replay usage examples for interactive SLURM allocation flows.
scripts/sglang_disagg/models.yaml Documents radix-cache expectations and how agentic replay strips the disable flag.
scripts/sglang_disagg/models.json Adds agentic-tagged model entries (Qwen3-32B, DeepSeek-V3 DP) for SGLang disagg.
scripts/sglang_disagg/benchmark_agentic.sh Adds a thin shim to the shared agentic benchmark runner for SGLang.
scripts/primus_train/run.sh Recognizes MaxDiffusion experiments and sets BACKEND accordingly.
scripts/primus_train/get_models_json.py Adds a primus_train default model, and excludes JAX backends to avoid duplicate discovery.
scripts/jax-maxtext/run.sh Replaces legacy model-specific logic with a Primus-based wrapper + perf extraction pipeline.
scripts/jax-maxtext/jax-maxtext_benchmark_setup.sh Removes legacy host-side MaxText setup script.
scripts/jax-maxtext/jax-maxtext_benchmark_report.sh Removes legacy host-side MaxText benchmark script.
scripts/jax-maxtext/jax-maxtext_benchmark_report.py Removes legacy perf parsing implementation (replaced by new extractor).
scripts/jax-maxtext/gpu-rocm/readme.md Removes legacy ROCm benchmarking docs for the old host-side scripts.
scripts/jax-maxtext/gpu-rocm/llama3.3_70b.sh Removes legacy host-side Docker benchmark script.
scripts/jax-maxtext/gpu-rocm/llama3_8b.sh Removes legacy host-side Docker benchmark script.
scripts/jax-maxtext/gpu-rocm/llama3_70b.sh Removes legacy host-side Docker benchmark script.
scripts/jax-maxtext/gpu-rocm/llama2_7b.sh Removes legacy host-side Docker benchmark script.
scripts/jax-maxtext/gpu-rocm/llama2_70b.sh Removes legacy host-side Docker benchmark script.
scripts/jax-maxtext/gpu-rocm/deepseek_v2_16b.sh Removes legacy host-side Docker benchmark script.
scripts/jax-maxtext/get_models_json.py Adds Primus MaxText config discovery as madengine models with arch/precision tagging and skips.
scripts/jax-maxtext/extract_maxtext_perf.py Adds a MaxText log parser that emits multiple_results CSV rows (tps/tflops).
scripts/jax-maxtext/env_scripts/qwen3_30b_a3b.yml Removes legacy env/config YAML.
scripts/jax-maxtext/env_scripts/qwen3_30b_a3b_env.sh Removes legacy env script.
scripts/jax-maxtext/env_scripts/qwen3_14b.yml Removes legacy env/config YAML.
scripts/jax-maxtext/env_scripts/qwen3_14b_env.sh Removes legacy env script.
scripts/jax-maxtext/env_scripts/mixtral_8x7b.yml Removes legacy env/config YAML.
scripts/jax-maxtext/env_scripts/mixtral_8x7b_env.sh Removes legacy env script.
scripts/jax-maxtext/env_scripts/llama3.3_70b.yml Removes legacy env/config YAML.
scripts/jax-maxtext/env_scripts/llama3.3_70b_env.sh Removes legacy env script.
scripts/jax-maxtext/env_scripts/llama3_8b.yml Removes legacy env/config YAML.
scripts/jax-maxtext/env_scripts/llama3_8b_env.sh Removes legacy env script.
scripts/jax-maxtext/env_scripts/llama3_70b.yml Removes legacy env/config YAML.
scripts/jax-maxtext/env_scripts/llama3_70b_env.sh Removes legacy env script.
scripts/jax-maxtext/env_scripts/llama2_7b.yml Removes legacy env/config YAML.
scripts/jax-maxtext/env_scripts/llama2_7b_env.sh Removes legacy env script.
scripts/jax-maxtext/env_scripts/llama2_70b.yml Removes legacy env/config YAML.
scripts/jax-maxtext/env_scripts/llama2_70b_env.sh Removes legacy env script.
scripts/jax-maxtext/env_scripts/gfx950_qwen3_30b_a3b.yml Removes legacy gfx950-specific env/config YAML.
scripts/jax-maxtext/env_scripts/gfx950_qwen3_30b_a3b_env.sh Removes legacy gfx950 env script.
scripts/jax-maxtext/env_scripts/gfx950_qwen3_14b.yml Removes legacy gfx950-specific env/config YAML.
scripts/jax-maxtext/env_scripts/gfx950_qwen3_14b_env.sh Removes legacy gfx950 env script.
scripts/jax-maxtext/env_scripts/gfx950_mixtral_8x7b.yml Removes legacy gfx950-specific env/config YAML.
scripts/jax-maxtext/env_scripts/gfx950_mixtral_8x7b_env.sh Removes legacy gfx950 env script.
scripts/jax-maxtext/env_scripts/gfx950_llama3.3_70b.yml Removes legacy gfx950-specific env/config YAML.
scripts/jax-maxtext/env_scripts/gfx950_llama3.3_70b_env.sh Removes legacy gfx950 env script.
scripts/jax-maxtext/env_scripts/gfx950_llama3.1_405b.yml Removes legacy gfx950-specific env/config YAML.
scripts/jax-maxtext/env_scripts/gfx950_llama3_8b.yml Removes legacy gfx950-specific env/config YAML.
scripts/jax-maxtext/env_scripts/gfx950_llama3_8b_env.sh Removes legacy gfx950 env script.
scripts/jax-maxtext/env_scripts/gfx950_llama3_70b.yml Removes legacy gfx950-specific env/config YAML.
scripts/jax-maxtext/env_scripts/gfx950_llama3_70b_env.sh Removes legacy gfx950 env script.
scripts/jax-maxtext/env_scripts/gfx950_llama2_7b.yml Removes legacy gfx950-specific env/config YAML.
scripts/jax-maxtext/env_scripts/gfx950_llama2_7b_env.sh Removes legacy gfx950 env script.
scripts/jax-maxtext/env_scripts/gfx950_llama2_70b.yml Removes legacy gfx950-specific env/config YAML.
scripts/jax-maxtext/env_scripts/gfx950_llama2_70b_env.sh Removes legacy gfx950 env script.
scripts/jax-maxtext/env_scripts/gfx950_deepseek2_env_16b.sh Removes legacy gfx950 env script.
scripts/jax-maxtext/env_scripts/gfx950_deepseek2_16b.yml Removes legacy gfx950-specific env/config YAML.
scripts/jax-maxtext/env_scripts/deepseek2_env_16b.sh Removes legacy env script.
scripts/jax-maxtext/env_scripts/deepseek2_16b.yml Removes legacy env/config YAML.
scripts/jax-maxdiffusion/run.sh Adds a Primus-based MaxDiffusion wrapper that caches weights sanely and extracts perf metrics.
scripts/jax-maxdiffusion/get_models_json.py Adds Primus MaxDiffusion config discovery as madengine models.
scripts/common/validate_agentic_result.sh Adds a post-run validator for agentic aggregate JSON (error rate + cache hit).
scripts/common/preflight_weights.sh Adds a pre-submit SLURM preflight to ensure weights are present on all selected nodes.
scripts/common/agentx/tests/test_offline.yaml Adds AgentX offline test config for source=corpus and source=profile paths.
scripts/common/agentx/tests/submit_matrix.sh Adds a submit-only campaign driver that fans out agentic cells via sbatch.
scripts/common/agentx/tests/resolve_ctx_offline.sh Adds offline tests for served-context-window resolution logic (no GPU required).
scripts/common/agentx/tests/harvest.sh Adds a read-only harvester to summarize per-cell suite results into a table.
scripts/common/agentx/tests/_stub_server.py Adds a minimal stdlib HTTP stub for offline resolver tests.
scripts/common/agentx/templates/README.md Documents how to integrate new backends with the shared agentic runner.
scripts/common/agentx/profiles/small.yaml Adds a small synthetic profile for testing AgentX generation/verification.
scripts/common/agentx/profiles/inferencex_256k.yaml Adds an HF preset for the canonical inferencex 256k trace corpus.
scripts/common/agentx/profiles/custom.example.yaml Adds an annotated template for custom AgentX workload profiles.
scripts/common/agentx/profiles/conformance_512k.yaml Adds the Case-B conformance profile (512k tail).
scripts/common/agentx/profiles/conformance_256k.yaml Adds the Case-A conformance profile (256k tail).
scripts/common/agentx/filter_weka_corpus.py Adds a stdlib-only tool to trim/filter weka_trace corpora into aiperf session files.
scripts/common/agentx/agentic.example.yaml Adds an end-to-end example AgentX suite configuration file.
models.json Clears the root model catalog.
docker/sglang_disagg_inference_full_overlay.ubuntu.amd.Dockerfile Adds an in-Docker build guard to avoid GPU-less gtest discovery failures in MoRI UMBP tests.
docker/primus_maxtext.ubuntu.amd.Dockerfile Adds a Primus MaxText image that bakes Primus onto a JAX MaxText base image.
docker/primus_maxdiffusion.ubuntu.amd.Dockerfile Adds a Primus MaxDiffusion image that bakes Primus onto a base with patched maxdiffusion.
docker/jax_maxtext.ubuntu.amd.Dockerfile Removes the older JAX MaxText Dockerfile (superseded by primus_maxtext).
.gitignore Ignores Primus checkout and large run artifacts to prevent accidental commits.
.dockerignore Prevents large runtime artifacts (hf_cache, logs, etc.) from bloating Docker build context.
Review details
  • Files reviewed: 124/125 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread models.json
Comment thread tools/fetch_primus.sh
@i-kosarev i-kosarev mentioned this pull request Sep 2, 2026
1 task
@i-kosarev i-kosarev changed the title Sync mad-rccl with develop (conflicts resolved), and unbreak the MoRI overlay build Sync mad-rccl with develop Sep 2, 2026
@i-kosarev
i-kosarev requested a balanced review from Copilot and removed request for Cemberk, amathews-amd, coketaste, gargrahul, itej89, lcskrishna and leconcio September 2, 2026 21:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Several launch and validation paths can hide benchmark failures, skip target nodes, or omit required metrics.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 124/125 changed files
  • Comments generated: 11
  • Review effort level: Balanced

Comment thread scripts/vllm_dissag/vllm_disagg.sh
Comment thread scripts/common/agentx/tests/harvest.sh
Comment thread scripts/common/preflight_weights.sh
Comment thread scripts/common/validate_agentic_result.sh
Comment thread scripts/jax-maxdiffusion/run.sh
Comment thread scripts/vllm_dissag/benchmark_niah.sh
Comment thread scripts/vllm_dissag/parse_to_csv.py
Comment thread tools/fetch_primus.sh
Comment thread scripts/sglang_disagg/salloc_launch.sh
Comment thread scripts/sglang_disagg/sglang_disagg_mori_io_ep.sh Outdated
Resolves the three conflicts blocking ROCm#244 and folds in one fix that the same
merge makes newly relevant.

Conflicts, all "both sides added here":
  - .gitignore: keep both rule blocks (skill manifest templates from mad-rccl,
    the JAX/MaxText and madengine-artifact rules from develop).
  - scripts/sglang_disagg/models.json: reconciled by unioning the card lists by
    name rather than editing markers, then checked nothing either side added was
    dropped. 21 + 18 over a common 16 -> 23 cards; develop contributes
    pyt_sglang_disagg_mori_io_agentic_qwen3-32b and
    pyt_sglang_disagg_mori_dp_agentic_deepseek-v3.
  - scripts/sglang_disagg/sglang_disagg_mori_io_ep.sh: take develop on both
    hunks. The first is a pure addition (agentic Prometheus metrics). The second
    is develop generalizing the hardcoded benchmark_xPyD.sh into ${_bench_file}
    for the AgentX suite; that variable is defined a few lines up by another,
    non-conflicting develop hunk and defaults to benchmark_xPyD.sh, so taking
    develop's side is backwards compatible.

Verified after resolution: no markers left, the shell script and both JSON files
parse, and both sides' markers survive in the launcher -- mad-rccl's arch gate,
Kimi MLA guard and the three checked/bounded barrier calls, alongside develop's
AGENTIC_SERVER_METRICS and BENCHMARK_SCRIPT_FILE hook.

Also re-gates mori's UMBP unit tests behind BUILD_TESTS in
sglang_disagg_inference_full_overlay (ported from MAD-private
aicomnet_dev_public_gpt_oss, bb7ce03). mori's src/umbp/CMakeLists.txt calls
add_subdirectory(tests) unconditionally; those tests use gtest_discover_tests()
in POST_BUILD mode, which EXECUTES each freshly-linked test binary to enumerate
cases. The binaries link the ROCm/HIP runtime and abort inside `docker build`,
which has no GPU, and the empty output then breaks CMake's string(JSON ...) and
fails the amd_mori wheel build. So ENABLE_MORI_OVERLAY=1 -- the documented knob
for A/B-ing a MoRI commit -- cannot currently build at its own default pin:
verified that the unconditional add_subdirectory(tests) is still present at
MORI_COMMIT=a14e6992 and already fixed on mori main. The patch greps before it
seds, so it self-disables to MORI_UMBP_TESTS_GUARD_SKIPPED once the pin moves
past the upstream fix. Not reproduced end to end here -- only the trigger
condition was confirmed -- and the default path (ENABLE_MORI_OVERLAY=0, MoRI
taken from the sgl-dev base) is unaffected either way.
Copilot AI review requested due to automatic review settings September 2, 2026 21:41
@i-kosarev
i-kosarev force-pushed the sync/develop-into-mad-rccl branch from ad7ca9c to 1a30229 Compare September 2, 2026 21:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The broad 94-file synchronization includes distributed launchers and an overlay build path that was not validated end to end.

Review details
  • Files reviewed: 124/125 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@i-kosarev
i-kosarev merged commit 62f3a49 into ROCm:mad-rccl Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants